package edu.neu.cs6515.hci;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.Timer;

import com.google.common.collect.Lists;

import edu.neu.cs6515.board.Hexagon;
import edu.neu.cs6515.board.HexagonBoard;
import edu.neu.cs6515.gui.CellRenderer;
import edu.neu.cs6515.gui.PlayerRenderer;
import edu.neu.cs6515.xml.Placement;
import edu.neu.cs6515.xml.Score;
import edu.neu.cs6515.xml.Tile;

/**
 * Event handling class for HCIPanel
 */
public class EventsCoordinator extends MouseAdapter implements ActionListener {

    private HCIPanel hciPanel;

    private Timer timer;

    private int intervalLeft;

    private HexagonBoard hexgonBoard;

    private List<Tile> tilesInHand;

    private final int interval;

	private List<CellRenderer> cellSelected;



    /**
     * C'tor of EventsCoordinator
     * 
     * @param hciStrategy
     *            the object to send resulting placement
     * @param hciPanel
     *            the object events come from
     * @param interval
     *            interval for timeout
     */
    public EventsCoordinator(final HCIPanel hciPanel, final int interval) {
        super();
        this.interval = interval;
        this.intervalLeft = interval;
        this.hciPanel = hciPanel;
        this.timer = new Timer(1000, this);
		this.cellSelected = new LinkedList<CellRenderer>();
    }

	/**
	 * @param e
	 *            mouse pressed listener for cells on board.
	 */
	public void mousePressed(MouseEvent e) {
		System.out.println("Mouse Pressed");
		CellRenderer cr = (CellRenderer) e.getSource();
		System.out.println("On Hex d:" + cr.getDistance() + " a:"
				+ cr.getAngle() + " color:" + cr.getIngeniousColor());
		
		cr.setSelected(!cr.isSelected());
		cr.repaint();
		if (cr.isSelected()) {
			// if an unSelected cell was Selected, it need to be added to the
			// cell select list.
			cellSelected.add(cr);
			if (cellSelected.size() >= 2) {
				submitConditionSatisfied();
				this.clearSelectedCells();
			}
		} else {
			this.clearSelectedCells();
		}
	}

	private void clearSelectedCells(){
		for (CellRenderer r : cellSelected) {
			r.setSelected(false);
			r.repaint();
		}
		cellSelected.clear();
	}

    /**
     * reset components in SetInputOutPutPane
     */
    private void reSetInputOutPutPane() {
        setOutputMessage("");
        setTimeMessage("");
        this.intervalLeft = this.interval;
        timer.start();
    }

    @Override
    public void actionPerformed(final ActionEvent ae) {
        if (ae.getSource().equals(this.timer)) {
            onTimerTick();
        }
    }


    /**
     * @param message
     *            text to be set to the messageLabel in hciPanel
     */
    private void setOutputMessage(final String message) {
        hciPanel.getMessageLabel().setText("message:" + message);
    }

    /**
     * @param message
     *            to be set
     */
    private void setTimeMessage(final String message) {
        hciPanel.getTimeLabel().setText("Secs Remaining:" + message);
    }

    /**
     * event handling method for timer event, count the time intervalLeft
     */
    private void onTimerTick() {
        setTimeMessage(String.valueOf(--this.intervalLeft));
        if (this.intervalLeft <= 0) {
            submitPlacement(new ArrayList<Placement>());
        }
    }

    /**
     * event handler when submit condition was met, that is 
	 * two cells were selected on board, and tile was selected in the menu list 
	 * this method validate the placement, and
     * hand it to HCIStrategy
     */
    private void submitConditionSatisfied() {
        int selectedIndex = hciPanel.getSelectedIndex();
        if (selectedIndex >= 0) {
            Tile tileSelected = this.tilesInHand.get(selectedIndex);
            String sD0 = String.valueOf(this.cellSelected.get(0).getDistance());
            String sA0 = String.valueOf(this.cellSelected.get(0).getAngle());
            String sD1 = String.valueOf(this.cellSelected.get(1).getDistance());
            String sA1 = String.valueOf(this.cellSelected.get(1).getAngle());
            try {
                Placement placement = new Placement(tileSelected.getC0(),
                        tileSelected.getC1(), Integer.parseInt(sD0),
                        Integer.parseInt(sA0), Integer.parseInt(sD1),
                        Integer.parseInt(sA1));
                if (this.hexgonBoard.getBrokenRules(placement)
                        .size() > 0) {
                    setOutputMessage("illegal placement");
                } else {
                    submitPlacement(Lists.newArrayList(placement));
                }
            } catch (NumberFormatException nfe) {
                setOutputMessage("input invalid");
            }
        }
    }

    private void submitPlacement(final List<Placement> p) {
        this.timer.stop();
        try {
			System.out.println("Submitting placement: " + p.get(0).toXMLString());
            hciPanel.getBlockingQueue().put(p);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    /**
     * update the hciPanel
     * 
     * @param tilesInHand
     * @param scoreBeforePlacement
     * @param hexagonBoard
     */
    public void onUpdate(final HexagonBoard hexagonBoard,
            final Score scoreBeforePlacement, final List<Tile> tilesInHand) {
        this.hexgonBoard = hexagonBoard;
        this.tilesInHand = tilesInHand;
		PlayerRenderer pr = hciPanel.getPlayerRenderer();
        pr.update(hexagonBoard,
                scoreBeforePlacement,
                tilesInHand);
		//set mouse listener to this for each cell on board
		Map<Hexagon,CellRenderer> m = pr.getHexToCellMap();
		for (Hexagon h : m.keySet()) {
			CellRenderer c = m.get(h);
			c.addMouseListener(this);
		}
        reSetInputOutPutPane();
    }

}
